import pandas as pd
import numpy as np
from sklearn import preprocessing
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_samples
from sklearn.metrics import silhouette_score
from sklearn.metrics import davies_bouldin_score
from sklearn.metrics import calinski_harabasz_score
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import seaborn as sns
from yellowbrick.cluster import KElbowVisualizer
from sklearn.manifold import TSNE
df = pd.read_csv('clusterization.csv')
# Análise da base a partir dos 5 primeiros registros
df.head()
# Análise dos Valores das Features da base
df.describe()
# Análise dos Valores das Features da base
df.info()
Como será utilizado um modelo baseado na distância euclidiana, é necessário verificar se os valor das features estão na mesma escala.
# Análise da Distribuição Original dos Valores da base
fig, (ax1) = plt.subplots(ncols=1, figsize=(20, 7))
ax1.set_title('Distribuição Original')
sns.kdeplot(df['x0'], ax=ax1)
sns.kdeplot(df['x1'], ax=ax1)
sns.kdeplot(df['x2'], ax=ax1)
sns.kdeplot(df['x3'], ax=ax1)
sns.kdeplot(df['x4'], ax=ax1)
sns.kdeplot(df['x5'], ax=ax1)
sns.kdeplot(df['x6'], ax=ax1)
sns.kdeplot(df['x7'], ax=ax1)
sns.kdeplot(df['x8'], ax=ax1)
sns.kdeplot(df['x9'], ax=ax1);
Como mostrado no acima, alguns valores não estão na mesma escala que os demais. Diante disso, será necessário normalizar os valores, de modo que os valores fiquem no mesmo intervalo do conjunto de treinamento, neste caso, entre zero e um. Para Isso, será utilizado o método MinMaxScaler da library sklearn. Abaixo é possível visualizar o processo.
# Transformação dos dados
mm_scaler = preprocessing.MinMaxScaler()
X = mm_scaler.fit_transform(df)
X = pd.DataFrame(X, columns=df.columns)
# Distribuição dos valores após a transformação
fig, (ax1) = plt.subplots(ncols=1, figsize=(20, 7))
ax1.set_title('Após o MinMaxScaler')
sns.kdeplot(X['x0'], ax=ax1)
sns.kdeplot(X['x1'], ax=ax1)
sns.kdeplot(X['x2'], ax=ax1)
sns.kdeplot(X['x3'], ax=ax1)
sns.kdeplot(X['x4'], ax=ax1)
sns.kdeplot(X['x5'], ax=ax1)
sns.kdeplot(X['x6'], ax=ax1)
sns.kdeplot(X['x7'], ax=ax1)
sns.kdeplot(X['x8'], ax=ax1)
sns.kdeplot(X['x9'], ax=ax1);
# Análise dos dados transformados
X.describe()
Nesta sessão, será utilizado o algoritmo KMeans, da library sklearn, para realizar o agrupamento. Serão utilizadas as seguintes métricas para avaliar no melhor número de clusters: elbow method, silhouette score, calinski-harabasz e davies-bouldin
# Elbow method
model = KMeans(random_state=10)
visualizer = KElbowVisualizer(model, k=(2,12))
plt.figure(figsize=(20, 7))
visualizer.fit(X)
visualizer.show();
Para avalição do Elbow method, foi utilizado o método KElbowVisualizer, que indica o número ideal de clusters, ajustando o modelo com uma gama de valores para K. Se o gráfico de linha se assemelha a um braço, o “cotovelo” (o ponto de inflexão na curva) é uma boa indicação de que o modelo trabalhado se ajusta melhor a esse ponto. Inicialmente, a medida de erro (variação dentro do cluster) diminui com o aumento no número do cluster. Após um determinado ponto, k, a medida de erro começa a achatar. O número do cluster correspondente a esse ponto específico, k = 4, deve ser considerado como o número ideal de clusters.
Para contribuir na análise, será executado o modelo TSNE de redução de dimensionalidade apenas para visualização dos dados. Esse modelo será útil para visualizar os grupos formados, através de um gráfico de dispersão.
# TSNE Redução de dimessionalidade para visualização
X_embedded = TSNE(n_components=2).fit_transform(X)
X_embedded.shape
# Silhouette score
range_n_clusters = range(2,12)
for n_clusters in range_n_clusters:
fig, (ax1, ax2) = plt.subplots(1, 2)
fig.set_size_inches(18, 7)
ax1.set_xlim([-0.1, 1])
ax1.set_ylim([0, len(X) + (n_clusters + 1) * 10])
clusterer = KMeans(n_clusters=n_clusters, random_state=10)
cluster_labels = clusterer.fit_predict(X)
silhouette_avg = silhouette_score(X, cluster_labels)
print('n_clusters =', n_clusters,' | Média de silhouette_score é :', silhouette_avg)
sample_silhouette_values = silhouette_samples(X, cluster_labels)
y_lower = 10
for i in range(n_clusters):
ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
ith_cluster_silhouette_values.sort()
size_cluster_i = ith_cluster_silhouette_values.shape[0]
y_upper = y_lower + size_cluster_i
color = cm.nipy_spectral(float(i) / n_clusters)
ax1.fill_betweenx(np.arange(y_lower, y_upper),
0, ith_cluster_silhouette_values,
facecolor=color, edgecolor=color, alpha=0.7)
ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
y_lower = y_upper + 10
ax1.set_title("The silhouette plot for the various clusters.")
ax1.set_xlabel("The silhouette coefficient values")
ax1.set_ylabel("Cluster label")
ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
ax1.set_yticks([])
ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])
# Segundo Plot
colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
ax2.scatter(X_embedded[:, 0], X_embedded[:, 1], marker='.', s=30, lw=0, alpha=0.7,
c=colors, edgecolor='k')
# Label
centers = clusterer.cluster_centers_
for i, c in enumerate(centers):
ax2.scatter(c[0], c[1], marker='$%d$' % i, alpha=1,
s=50, edgecolor='k')
ax2.set_title("The visualization of the clustered data.")
ax2.set_xlabel("Feature space for the 1st feature")
ax2.set_ylabel("Feature space for the 2nd feature")
plt.suptitle(("Análise de silhueta com Clusters = %d" % n_clusters),
fontsize=14, fontweight='bold')
plt.show()
Através do Silhouette score e de suas representações gráficas, é possível visualizar que o número de clusters 5 apresenta uma boa distância entre os grupos, deixando os grupos bem definidos.
range_n_clusters = range(2,12)
db={}
for n_clusters in range_n_clusters:
kmeans = KMeans(n_clusters=n_clusters,random_state=10).fit(X)
if n_clusters == 3: labels = kmeans.labels_
clusters = kmeans.labels_
db[n_clusters] = davies_bouldin_score(X,clusters)
print('n_cluster: ' + str(n_clusters) + ' | davies bouldin score: ' + str(db[n_clusters]))
plt.figure(figsize=(15,10))
plt.plot(list(db.keys()), list(db.values()))
plt.xlabel("Number of cluster")
plt.ylabel("Davies-Bouldin values")
plt.show()
range_n_clusters = range(2,12)
ch={}
for n_clusters in range_n_clusters:
kmeans = KMeans(n_clusters=n_clusters,random_state=10).fit(X)
if n_clusters == 5: labels = kmeans.labels_
clusters = kmeans.labels_
ch[n_clusters] = calinski_harabasz_score(X,clusters)
print('n_cluster: ' + str(n_clusters) + ' | calinski harabasz score: ' + str(ch[n_clusters]))
plt.figure(figsize=(15,10))
plt.plot(list(ch.keys()), list(ch.values()))
plt.xlabel("Number of cluster")
plt.ylabel("Calinski Harabasz values")
plt.show()
Com análise das métricas, é possível visualizar que o número ideal de clusters, para este problema, é o valor 5, pois é K que menos apresenta variação dentro dos clusters(medida de erro) e que apresenta melhor distância entre os grupos, como pode ser visto no gráfico de dispersão da seção 4.2. Dessa forma, obtém-se grupos bem definidos.